{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "06e5af38-85ad-46b6-a5ea-52a7ff947853",
   "metadata": {},
   "source": [
    "# Labelme转YOLO-批量\n",
    "\n",
    "同济子豪兄 2023-4-16"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0a8a2a1-fb46-4d51-8b4f-cd0568bc13f8",
   "metadata": {},
   "source": [
    "## 导入工具包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "5969d148-19e6-4b2e-9797-01105b87b56c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import json\n",
    "import shutil\n",
    "import numpy as np\n",
    "from tqdm import tqdm"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75c4fd53-f3ce-4a27-9024-6abec83717d9",
   "metadata": {},
   "source": [
    "## 删除系统自动生成的多余文件"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7ecd3ad-874c-46cc-ac47-6f4ae3adf07a",
   "metadata": {},
   "source": [
    "### 查看待删除的多余文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a8356247-4b89-42e9-aa87-4c630f05e1f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '__MACOSX'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a1b64d9d-bdf8-43fe-8321-bb6d241137dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.DS_Store'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a5f038c2-72ba-4724-a190-b2ec97d8bcf0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./.ipynb_checkpoints\n"
     ]
    }
   ],
   "source": [
    "!find . -iname '.ipynb_checkpoints'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "191ccc29-baeb-46ff-8125-71c49adee533",
   "metadata": {},
   "source": [
    "### 删除多余文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4bf04f95-0750-4248-8696-fa2aa4ff03e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "!for i in `find . -iname '__MACOSX'`; do rm -rf $i;done"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "217b2325-835b-451c-89d7-3d39c5dfc5c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "!for i in `find . -iname '.DS_Store'`; do rm -rf $i;done"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cfb7afa4-106a-4e6a-ab35-86f0eb902432",
   "metadata": {},
   "outputs": [],
   "source": [
    "!for i in `find . -iname '.ipynb_checkpoints'`; do rm -rf $i;done"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a2904f2-93ba-4726-9b2b-b8285511a760",
   "metadata": {},
   "source": [
    "### 验证多余文件已删除"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "33239471-a70e-426d-beba-3517c14311e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '__MACOSX'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f82a7703-8b94-4103-9672-ec29849616b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.DS_Store'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "0101460a-d0bc-4388-aa0b-d30b0f0ef538",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.ipynb_checkpoints'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "092bec89-6d83-4800-9cc9-3619896b2572",
   "metadata": {},
   "source": [
    "## 指定数据集信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "30a7e9be-197e-4a5a-af0b-c54226edf48d",
   "metadata": {},
   "outputs": [],
   "source": [
    "Dataset_root = 'Ruler_15_Dataset'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "79463161-8aa7-4980-8539-9539b7af3fdf",
   "metadata": {},
   "outputs": [],
   "source": [
    "classes = {\n",
    "    'Angle':0,\n",
    "    'Ruler':1,\n",
    "    'Triangle_30':2,\n",
    "    'Triangle_45':3\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67489bb1-f015-411d-983e-541651fd1af4",
   "metadata": {},
   "source": [
    "## 生成`classes.txt`文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "0dd3df94-953b-4025-9965-1a749fcbb83b",
   "metadata": {},
   "outputs": [],
   "source": [
    "os.chdir(Dataset_root)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "9bae1131-1409-4a33-b408-e2a6c7c119ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('classes.txt', 'w', encoding='utf-8') as f:\n",
    "    for each in list(classes.keys()):\n",
    "        f.write(each + '\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "39640f9b-ed40-4bcd-8a62-cec29e95d64d",
   "metadata": {},
   "outputs": [],
   "source": [
    "os.mkdir('labels')\n",
    "os.mkdir('labels/train')\n",
    "os.mkdir('labels/val')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "404c8abc-20d7-491c-a24c-c74d790f56c5",
   "metadata": {},
   "source": [
    "## 函数-处理单个labelme标注json文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5434a38c-7e2d-44c6-9eb4-eafa1c3e0cd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_single_json(labelme_path, save_folder='../../labels/train'):\n",
    "    \n",
    "    # 载入 labelme格式的 json 标注文件\n",
    "    with open(labelme_path, 'r', encoding='utf-8') as f:\n",
    "        labelme = json.load(f)\n",
    "        \n",
    "    img_width = labelme['imageWidth']   # 图像宽度\n",
    "    img_height = labelme['imageHeight'] # 图像高度\n",
    "    \n",
    "    # 生成 YOLO 格式的 txt 文件\n",
    "    suffix = labelme_path.split('.')[-2]\n",
    "    yolo_txt_path = suffix + '.txt'\n",
    "    \n",
    "    with open(yolo_txt_path, 'w', encoding='utf-8') as f:\n",
    "        for each_ann in labelme['shapes']: # 遍历每个框\n",
    "\n",
    "            if each_ann['shape_type'] == 'rectangle': # 筛选出框\n",
    "\n",
    "                # 获取类别 ID\n",
    "                bbox_class_id = classes[each_ann['label']]\n",
    "\n",
    "                # 左上角和右下角的 XY 像素坐标\n",
    "                bbox_top_left_x = int(min(each_ann['points'][0][0], each_ann['points'][1][0]))\n",
    "                bbox_bottom_right_x = int(max(each_ann['points'][0][0], each_ann['points'][1][0]))\n",
    "                bbox_top_left_y = int(min(each_ann['points'][0][1], each_ann['points'][1][1]))\n",
    "                bbox_bottom_right_y = int(max(each_ann['points'][0][1], each_ann['points'][1][1]))\n",
    "\n",
    "                # 框中心点的 XY 像素坐标\n",
    "                bbox_center_x = int((bbox_top_left_x + bbox_bottom_right_x) / 2)\n",
    "                bbox_center_y = int((bbox_top_left_y + bbox_bottom_right_y) / 2)\n",
    "\n",
    "                # 框宽度\n",
    "                bbox_width = bbox_bottom_right_x - bbox_top_left_x\n",
    "\n",
    "                # 框高度\n",
    "                bbox_height = bbox_bottom_right_y - bbox_top_left_y\n",
    "\n",
    "                # 框中心点归一化坐标\n",
    "                bbox_center_x_norm = bbox_center_x / img_width\n",
    "                bbox_center_y_norm = bbox_center_y / img_height\n",
    "\n",
    "                # 框归一化宽度\n",
    "                bbox_width_norm = bbox_width / img_width\n",
    "                # 框归一化高度\n",
    "                bbox_height_norm = bbox_height / img_height\n",
    "\n",
    "                # 生成 YOLO 格式的一行标注，指定保留小数点后几位\n",
    "                bbox_yolo_str = '{} {:.4f} {:.4f} {:.4f} {:.4f}'.format(bbox_class_id, bbox_center_x_norm, bbox_center_y_norm, bbox_width_norm, bbox_height_norm)\n",
    "                # 写入 txt 文件中\n",
    "                f.write(bbox_yolo_str + '\\n')\n",
    "\n",
    "    shutil.move(yolo_txt_path, save_folder)\n",
    "    print('{} --> {} 转换完成'.format(labelme_path, yolo_txt_path))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8db18e73-840b-4075-bdb0-b818679ece19",
   "metadata": {},
   "source": [
    "## 转换训练集标注文件至`labels/train`目录"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "06c90b4d-cc48-4424-9795-74467d1f3e58",
   "metadata": {},
   "outputs": [],
   "source": [
    "os.chdir('labelme_jsons/train')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "166948af-0194-46ad-98c2-9cc4f6771cb1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11.json --> 11.txt 转换完成\n",
      "12.json --> 12.txt 转换完成\n",
      "5.json --> 5.txt 转换完成\n",
      "1.json --> 1.txt 转换完成\n",
      "6.json --> 6.txt 转换完成\n",
      "9.json --> 9.txt 转换完成\n",
      "7.json --> 7.txt 转换完成\n",
      "8.json --> 8.txt 转换完成\n",
      "4.json --> 4.txt 转换完成\n",
      "15.json --> 15.txt 转换完成\n",
      "10.json --> 10.txt 转换完成\n",
      "14.json --> 14.txt 转换完成\n",
      "YOLO格式的txt标注文件已保存至  ../../labels/train\n"
     ]
    }
   ],
   "source": [
    "save_folder = '../../labels/train'\n",
    "for labelme_path in os.listdir():\n",
    "    process_single_json(labelme_path, save_folder=save_folder)\n",
    "print('YOLO格式的txt标注文件已保存至 ', save_folder)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "5ed297c2-e715-42c9-80ab-fd9e886c3385",
   "metadata": {},
   "outputs": [],
   "source": [
    "os.chdir('../../')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82a4746c-8980-4763-b8b5-86b398f12b96",
   "metadata": {},
   "source": [
    "## 转换测试集标注文件至`labels/val`目录"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "46914ef6-f6a7-43a3-8270-0c5e40300c1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "os.chdir('labelme_jsons/val')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "20a6ca8c-e004-46d3-abdf-b786ba9fcea2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.json --> 3.txt 转换完成\n",
      "13.json --> 13.txt 转换完成\n",
      "2.json --> 2.txt 转换完成\n",
      "YOLO格式的txt标注文件已保存至  ../../labels/val\n"
     ]
    }
   ],
   "source": [
    "save_folder = '../../labels/val'\n",
    "for labelme_path in os.listdir():\n",
    "    process_single_json(labelme_path, save_folder=save_folder)\n",
    "print('YOLO格式的txt标注文件已保存至 ', save_folder)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "6874920e-c01d-4ff3-8e05-2462669000cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "os.chdir('../../')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02d14035-e745-403e-8519-241c9f05deaa",
   "metadata": {},
   "source": [
    "## 删除labelme格式的标注文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "9eb2bf84-99a6-42d5-b636-d10380b1018d",
   "metadata": {},
   "outputs": [],
   "source": [
    "!rm -rf labelme_jsons"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67efa3f5-8eac-4830-868c-a8b650211546",
   "metadata": {
    "tags": []
   },
   "source": [
    "## 删除系统自动生成的多余文件"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eee7b8d7-a04f-4f12-81b8-09f17cf855b2",
   "metadata": {},
   "source": [
    "### 查看待删除的多余文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "1fd91717-f871-46ea-ae4b-17310320aad4",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '__MACOSX'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "a3949cdb-77f4-4caa-a41b-20bc2f7de3e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.DS_Store'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "ed8d61b9-4d8c-4e10-acdf-4d988a8a222d",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.ipynb_checkpoints'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "811a56c6-6f55-4bc9-8931-052ea77b42d1",
   "metadata": {},
   "source": [
    "### 删除多余文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "c1fa80b5-8aad-4cf4-b35a-93dc7f73c7bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "!for i in `find . -iname '__MACOSX'`; do rm -rf $i;done"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "b1155953-ca52-4308-ad72-8a4b6b845649",
   "metadata": {},
   "outputs": [],
   "source": [
    "!for i in `find . -iname '.DS_Store'`; do rm -rf $i;done"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "7fbcd3d4-0b45-4783-96dd-c7c3ed5d4906",
   "metadata": {},
   "outputs": [],
   "source": [
    "!for i in `find . -iname '.ipynb_checkpoints'`; do rm -rf $i;done"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d02b9016-5699-457a-80d2-5d7ef7540fd1",
   "metadata": {},
   "source": [
    "### 验证多余文件已删除"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "8ff22d29-4343-4c57-938f-b023595e026d",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '__MACOSX'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "87831673-8a64-411e-888d-a2694b00a8da",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.DS_Store'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "6c6b5580-5250-4ad0-aff9-8bcb8097dfd7",
   "metadata": {},
   "outputs": [],
   "source": [
    "!find . -iname '.ipynb_checkpoints'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "761e509a-6b5a-4721-ae09-f8254f152d05",
   "metadata": {},
   "source": [
    "## 得到完整的YOLO格式数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "77c362f0-fbe0-405e-a6a0-316099da5bf9",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
